package cc.magicjson.summary.errors.connection.closed.service.impl;

import cc.magicjson.summary.errors.connection.closed.entity.User;
import cc.magicjson.summary.errors.connection.closed.mapper.UserMapper;
import cc.magicjson.summary.errors.connection.closed.monitor.MethodStopWatch;
import cc.magicjson.summary.errors.connection.closed.service.UserService;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link}
 *
 * @author <a href="mailto:it_magicjson@163.com">MagicJson</a>
 * @since 1.0.0
 */
@Service
public class UserServiceImpl implements UserService {
    private final SqlSessionFactory sqlSessionFactory;
    private final UserMapper userMapper;
    private final MethodStopWatch stopWatch;
    private final JdbcTemplate jdbcTemplate;
    private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    public UserServiceImpl(SqlSessionFactory sqlSessionFactory, UserMapper userMapper, MethodStopWatch stopWatch, JdbcTemplate jdbcTemplate) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.userMapper = userMapper;
        this.stopWatch = stopWatch;
        this.jdbcTemplate = jdbcTemplate;
        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void insertByXml(User user) {
        insertByXmlPrivate(user);
    }

    private void insertByXmlPrivate(User user) {
        userMapper.insertByXml(user);
    }

    @Override
    public void simulateConnectionClosed() throws SQLException {

    }

    @Override
    public List<User> findAll() {
        return stopWatch.monitorWithTable("user", () ->
            userMapper.findAll()
        );
    }

    @Override
    public void batchInsertWithDelay(List<User> users) throws InterruptedException {

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchInsert(List<User> users) {
        stopWatch.monitorWithTable("user", "batchInsert->sqlSession.batchInsert", () -> {
            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
            try {
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                for (int i = 0; i < users.size(); i++) {
                    mapper.insert(users.get(i));
                    if (i % 1000 == 0 || i == users.size() - 1) {
                        sqlSession.flushStatements();
                    }
                }
                sqlSession.commit();
            } finally {
                sqlSession.close();
            }
            return users;
        });
        System.out.println("影响行数:" + findAll().size());
        throw new RuntimeException("Transaction rollback");
    }

    @Override
    public void insertOne(User user) {
        userMapper.insert(user);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchInsertWithJdbcTemplate(List<User> users){
        batchInsertWithJdbcTemplateNoTransaction(users);
    }


    public void batchInsertWithJdbcTemplateNoTransaction(List<User> users) {
        stopWatch.monitorWithTable("user", "batchInsert->jdbcTemplate", () -> {
            // 方式1：使用普通JdbcTemplate
            String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
            jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    User user = users.get(i);
                    ps.setString(1, user.getName());
                    ps.setString(2, user.getEmail());
                }

                @Override
                public int getBatchSize() {
                    return users.size();
                }
            });
            return users;
        });
        System.out.println("影响行数:" + findAll().size());
        throw new RuntimeException("Transaction rollback");
    }

    @Override
    public void batchInsertWithNamedParameter(List<User> users) {
        stopWatch.monitorWithTable("user", "batchInsert->namedJdbcTemplate", () -> {
            // 方式2：使用NamedParameterJdbcTemplate
            String sql = "INSERT INTO users (name, email) " +
                        "VALUES (:name, :email)";

            SqlParameterSource[] batchParams = users.stream()
                .map(user -> new MapSqlParameterSource()
                    .addValue("name", user.getName())
                    .addValue("email", user.getEmail())
                    )
                .toArray(SqlParameterSource[]::new);

            namedParameterJdbcTemplate.batchUpdate(sql, batchParams);
            return users;
        });
    }

    @Override
    public void batchInsertWithRewriteBatchedStatements(List<User> users) {
        stopWatch.monitorWithTable("user", "batchInsert->rewriteBatchedStatements", () -> {
            // 方式3：使用rewriteBatchedStatements优化（需要在JDBC URL中添加rewriteBatchedStatements=true）
            String sql = "INSERT INTO users (name, email) VALUES (?, ?)";

            int batchSize = calculateOptimalBatchSize(users);
            List<List<User>> batches = splitIntoBatches(users, batchSize);

            for (List<User> batch : batches) {
                jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        User user = batch.get(i);
                        ps.setString(1, user.getName());
                        ps.setString(2, user.getEmail());
                    }
                    @Override
                    public int getBatchSize() {
                        return batch.size();
                    }
                });
            }
            return users;
        });
    }

    @Override
    public Integer deleteAll() {
        return stopWatch.monitorWithTable("user", "deleteAll",() -> userMapper.deleteAll());
    }

    // 辅助方法：计算最优批次大小
    private int calculateOptimalBatchSize(List<User> users) {
        // 可以使用之前讨论的BatchInsertOptimizer
        return Math.min(1000, users.size());
    }

    // 辅助方法：将列表分割成批次
    private <T> List<List<T>> splitIntoBatches(List<T> items, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < items.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, items.size());
            batches.add(items.subList(i, endIndex));
        }
        return batches;
    }
}
